<?php
/**
 * Created by JetBrains PhpStorm.
 * User: xx
 * Date: 14-8-8
 * Time: 下午2:27
 * To change this template use File | Settings | File Templates.
 */

class Schedule
{
    var $CI;
    var $local_time; //时间

    var $code_list; //任务队列， 用于通过作业codes查找
    var $work_list; //排队队列
    var $pipeline; //工作流水线
    var $hang_up_list; //挂起队列

    var $persMod;
    var $pipeMod;
    var $workMod;

    public function __construct($config = array())
    {
        $this->CI =& get_instance();
        $this->local_time = time();
        if (count($config) > 0) {
            $this->initialize($config);
        }

        $this->CI->load->model('persistence_model', 'persMod');
        $this->persMod = & $this->CI->persMod;

        $this->CI->load->model('pipeline_model', 'pipeMod');
        $this->pipeMod = & $this->CI->pipeMod;

        $this->CI->load->model('work_model', 'workMod');
        $this->workMod = & $this->CI->workMod;

        $this->load();
    }

    /**
     * 加载
     */
    public function load()
    {
        //任务队列， 用于通过作业codes查找
        $this->code_list = array();
        $ret = $this->persMod->get_pers_data('code_list');
        if ($ret->get_code() === 0) {
            $code_list = $ret->get_data();
            if ($code_list) {
                $this->code_list = $code_list;
            }
        }

        $this->work_list = array(
            'max_index' => 1, //最大下标
            'count' => 0, //队列的总长度
            'start' => 0, //起始下标
            'db' => array(), //任务数据，存在任务有详细内容
        );
        $ret = $this->persMod->get_pers_data('work_list');
        if ($ret->get_code() === 0) {
            $work_list = $ret->get_data();
            if ($work_list) {
                $this->work_list = $work_list;
            }
        }

        $this->pl_load();

        $this->hu_load();
    }

    /**
     * 持久
     */
    public function persistence()
    {
        $this->persMod->persistence('code_list', $this->code_list);
        $this->persMod->persistence('work_list', $this->work_list);
        $this->pl_persistence();
        $this->hu_persistence();
    }

    /**
     * 刷新状态，查看是否有完成单
     * @param $status
     */
    public function update_status($status)
    {
        if ($status['order_state'] == 0) { //作费
            $this->cl_update_status($status['order_sn'], 5);
        } else if ($status['order_state'] == 30) { //完成
            $this->cl_update_status($status['order_sn'], 4);
        } else { //待定
            $this->cl_update_status($status['order_sn'], 4);
        }

    }

    /**
     * 刷新
     */
    public function refresh()
    {
        foreach ($this->pipeline['db'] as $pipe_name => $pipe) {
            $this->pl_refresh($pipe_name);
        }
        $this->hu_refresh();
    }

    /**
     * 分配作业
     */
    public function divider()
    {
        do {
            $flag = false;
            foreach ($this->pipeline['db'] as $pipe_name => $pipe) {
                $ret = $this->pl_add($pipe_name);
                $flag = $flag || $ret;
            }
        } while ($flag);
    }

    public function get_work_list()
    {
        $node_code = array();
        $p = $this->work_list['start'];
        do {
            $node_tmp = $this->wl_get($p);
            $node_code[] = $node_tmp['node_code'];
            $p = $node_tmp['next_p'];

        } while ($p > 0);

        return $node_code;
    }

    /****************************************************************************************************/
    // code_list队列
    /****************************************************************************************************/

    public function cl_add($node, $index, $info)
    {
        if (isset($this->code_list[$node['node_code']])) {
            return false;
        } else {
            $this->code_list[$node['node_code']] = array(
                'index' => $index,
                'order_id' => $info['order_id'],
                'work_id' => $info['work_id'], //在系统数据库中的ID
                'work_cyle' => $info['work_cyle'], //作业时长
                'status' => 0, //状态
            );
            return true;
        }
    }

    public function cl_remove($node_code)
    {
        unset($this->code_list[$node_code]);
    }

    public function cl_get_all_codes()
    {
        $order_id_str = false;
        $order_id_arr = array();
        foreach ($this->code_list as $codeNode) {
            $order_id_arr[] = $codeNode['order_id'];
        }
        if ($order_id_arr) {
            $order_id_str = implode(',', $order_id_arr);
        }
        return $order_id_str;
    }

    /**
     * 修改状态
     * @param $node_code
     * @param $new_status
     */
    public function cl_update_status($node_code, $new_status, $work_pipe = '')
    {
        if (isset($this->code_list[$node_code])) {
            $old_status = $this->code_list[$node_code]['status'];
            $this->code_list[$node_code]['status'] = $new_status;
            $this->workMod->update_status($this->code_list[$node_code]['work_id'], $old_status, $new_status, $this->local_time, $work_pipe);
            if ($new_status == 4 || $new_status == 5) {
                $this->hu_remove($node_code);
                $this->pl_remove($node_code);

                $wl_node = $this->wl_get_by_code($node_code);
                $this->wl_remove($wl_node['index']);

                $this->cl_remove($node_code);
            }
        }
    }

    // code_list队列 end
    /****************************************************************************************************/

    /****************************************************************************************************/
    // 排队队列
    /****************************************************************************************************/
    public function wl_create_node($node)
    {
        $work_node = array(
            'node_weight' => 0, //权重
            'node_code' => '', //作业编号 (订单号)
            'next_p' => 0, //下一个node的下标
            'prev_p' => 0, //上一个node的下标
        );

        $work_node['node_weight'] = $node['node_weight'];
        $work_node['node_code'] = $node['node_code'];
        return $work_node;
    }

    /**
     * 作业入队，根据权重插入到合适的位置
     * @param $node_weight 权重
     * @param $node 作业
     * @param $info 数据表中的信息
     * @return bool
     */
    public function wl_add($node_weight, $node, $info)
    {
        //判断订单号是否已存在
        if (!$this->cl_add($node, $this->work_list['max_index'], $info)) {
            return false;
        }
        $this->work_list['db'][$this->work_list['max_index']] = $node;

        if ($this->work_list['start'] > 0) {
            //遍历排队队列
            $p = $this->work_list['start'];
            do {
                $node_tmp = $this->wl_get($p);
                if ($node_weight > $node_tmp['node_weight']) { //插入
                    if ($p == $this->work_list['start']) { //插入首位
                        $this->work_list['db'][$p]['prev_p'] = $this->work_list['max_index']; //设置原首位的 prev 
                        $this->work_list['db'][$this->work_list['max_index']]['next_p'] = $this->work_list['start']; //本节点的next
                        $this->work_list['start'] = $this->work_list['max_index'];
                    } else { //插入中间位
                        $this->work_list['db'][$node_tmp['prev_p']]['next_p'] = $this->work_list['max_index']; //当前节点的上一个结点的next_p是自己
                        $this->work_list['db'][$this->work_list['max_index']]['prev_p'] = $node_tmp['prev_p']; //自己节点的上一个结点是当前结点的上一个节点
                        $this->work_list['db'][$this->work_list['max_index']]['next_p'] = $p; //自己节点的下一个结点是当前节点
                        $this->work_list['db'][$p]['prev_p'] = $this->work_list['max_index']; //当前节点的prev_p是自己
                    }
                    break; //跳出while
                }
                if ($node_tmp['next_p'] <= 0) { //$p起到队列最后，说明待插入节点在最后
                    $this->work_list['db'][$p]['next_p'] = $this->work_list['max_index']; //当前节点的next_p是自己
                    $this->work_list['db'][$this->work_list['max_index']]['prev_p'] = $p; //自己节点的上一个节点是当前节点
                    break; //跳出while
                }
                $p = $node_tmp['next_p'];
            } while ($node_tmp['next_p'] > 0);

        } else {
            $this->work_list['start'] = $this->work_list['max_index'];
        }

        $this->work_list['count']++; //队列的总长度 +1
        $this->work_list['max_index']++; //最大下标 +1

        //修改状态 一般为从  0 -> 1
        $this->cl_update_status($node['node_code'], 1);
        return true;
    }

    /**
     * 根据编号从队列中删除一个作业
     * 一般用于作废一个作业 和 移动节点
     * @param $index 下标
     */
    public function wl_out_list($index)
    {
        $rm_node = $this->wl_get($index);
        if ($rm_node) {
            //如果删除的的第一个 重置start
            if ($index == $this->work_list['start']) {
                $this->work_list['start'] = $rm_node['next_p'];
            } else if ($rm_node['next_p'] <= 0) { //如果删除的是最后一个
                $this->work_list['db'][$rm_node['prev_p']]['next_p'] = 0;
            } else { //如果删除的中
                $this->work_list['db'][$rm_node['prev_p']]['next_p'] = $rm_node['next_p'];
                $this->work_list['db'][$rm_node['next_p']]['prev_p'] = $rm_node['prev_p'];
            }

            $this->work_list['db'][$index]['next_p'] = 0;
            $this->work_list['db'][$index]['prev_p'] = 0;
        }

    }

    /**
     * 根据编号从队列中删除一个作业,并清理数据
     * 一般用于作废一个作业
     * @param $index 下标
     */
    public function wl_remove($index)
    {
        $this->wl_out_list($index);

        $del_node = $this->wl_get($index);
        if ($del_node) {
            $this->work_list['count']--; //队列的总长度 -1
//            $this->cl_remove($del_node['node_code']);
            unset($this->work_list['db'][$index]);
        }
    }

    /**
     * 调整作业在队列中的位置
     * 一般用于人工操作
     * @param $from 原位置 在队列中的下标
     * @param $to 新位置 在队列中的下标 一般认为是将$from节点放在$to节点以前，若$to为0，则将$from节点移动到最后
     */
    public function wl_adjust($from, $to)
    {
        $from_node = $this->wl_get($from);
        $to_node = $this->wl_get($to);
        if ($from_node && $to_node) {
            //$from出队
            $this->wl_out_list($from);
            $to_node = $this->wl_get($to);

            if ($to == $this->work_list['start']) { //移动到最队首
                $this->work_list['start'] = $from;
                $this->work_list['db'][$from]['next_p'] = $to;
                $this->work_list['db'][$to]['prev_p'] = $from;
            } else {
                $this->work_list['db'][$from]['prev_p'] = $to_node['prev_p'];
                $this->work_list['db'][$to_node['prev_p']]['next_p'] = $from;

                $this->work_list['db'][$from]['next_p'] = $to;
                $this->work_list['db'][$to]['prev_p'] = $from;
            }
        } else if ($from_node && $to === 0) { //将$from节点移动到最后
            //$from出队
            $this->wl_out_list($from);

            //遍历排队队列
            $p = $this->work_list['start'];
            do {
                $node_tmp = $this->wl_get($p);
                if ($node_tmp['next_p'] == 0) { //已经到了最后的一个节点
                    $this->work_list['db'][$p]['next_p'] = $from;
                    $this->work_list['db'][$from]['prev_p'] = $p;
                    break; //退出while
                } else {
                    $p = $node_tmp['next_p'];
                }
            } while ($p > 0);
        }


    }

    /**
     * 编辑作业内容
     * @param $index
     * @param $node
     */
    public function wl_edit($index, $node)
    {

    }

    /**
     * 根据下标获得作业
     * 不从排队队列中删除
     * @param $index
     * @return null
     */
    public function wl_get($index)
    {
        $node = false;
        if ($index > 0 && $index < $this->work_list['max_index'] && isset($this->work_list['db'][$index])) {
            $node = $this->work_list['db'][$index];
        }
        return $node;
    }

    /**
     * 根据作业编号获得作业
     * 不从排队队列中删除
     * @param $node_code 作业编号（订单号）
     * @return false
     */
    public function wl_get_by_code($node_code)
    {
        $node = false;
        if (isset($this->code_list[$node_code])) {
            $index = $this->code_list[$node_code]['index'];
            $node = $this->wl_get($index);
            $node['index'] = $index;
            return $node;
        }
        return $node;
    }

    /**
     * 获取排队队列第一人个作业，并从队列中删除
     * @return false
     */
    public function wl_get_first()
    {
        $node = false;
        if ($this->work_list['start'] > 0 && $this->work_list['count'] > 0) {
            $node = $this->wl_get($this->work_list['start']);
            $this->wl_remove($this->work_list['start']);
        }
        return $node;
    }


    // 排队队列 end
    /****************************************************************************************************/


    /****************************************************************************************************/
    // 流水线
    /****************************************************************************************************/
    public function pl_load()
    {
        $this->pipeline = array(
            'work_count' => 0, //全部流水线上当前作业数
            'pipe_count' => 0, //流水线 条数
            'db' => array(), //流水线
        );
        $ret = $this->pipeMod->get_pipe_data();
        if ($ret->get_code() === 0) {
            $pipe_arr = $ret->get_data();
            foreach ($pipe_arr as $pipe_node) {
                $pipe = array(
                    'status' => $pipe_node['pipe_status'], //状态 0:暂停 1：开启
                    'max' => $pipe_node['pipe_max'], //流水线长度，同时最大的工作数
                    'count' => 0, //流水线上当前作业数
                    'current' => 0, //当前工作的 index_db 下标， 体现在 走时 上，通常情况都为0，即第一位上的工作走时
                    'index_db' => array(), //流水线上的作业，对应任务在work_list中的下标的数据,数据长度为  max
                );
                $pipe_ret = $this->persMod->get_pers_data('pipe_' . $pipe_node['pipe_name']);
                if ($pipe_ret->get_code() === 0) {
                    $pipe = $pipe_ret->get_data();
                    if ($pipe) {
                        $pipe['status'] = $pipe_node['pipe_status'];
                        $pipe['max'] = $pipe_node['pipe_max'];
                    }
                }
                $this->pipeline['db'][$pipe_node['pipe_name']] = $pipe;
                $this->pipeline['work_count'] += $pipe['count'];
                $this->pipeline['pipe_count']++;
            }
        }
    }

    /**
     * 流水线持久
     */
    public function pl_persistence()
    {
        foreach ($this->pipeline['db'] as $pipe_name => $pipe) {
            $this->persMod->persistence('pipe_' . $pipe_name, $pipe);
        }
    }

    /**
     * @param $work_node array('node_code', 'work_cyle');
     * @return array
     */
    public function pl_create_node($work_node)
    {
        $cl_node = $this->code_list[$work_node['node_code']];

        $pipe_node = array(
            'start_time' => 0, //开始计时时间，只有排在第一位
            'cyle_time' => $cl_node['work_cyle'], //最大作业时长
            'use_time' => 0, //已工作时间
            'node_code' => $work_node['node_code'], // 对应任务在work_list中的下标的数据
        );

        return $pipe_node;
    }

    /**
     * 正常插入流水线，判断流水线状态和流水线是否有空位
     * @param $pipe_name
     * @param $work_node 当为空时，自己需求
     * @return bool
     */
    public function pl_add($pipe_name, $work_node = false)
    {
        $pipe = & $this->pipeline['db'][$pipe_name];
        if ($pipe['status'] == 1 && $pipe['count'] < $pipe['max']) {
            if (!$work_node) {
                $work_node = $this->wl_get_first();
                if (!$work_node) { //没有可分配的作业了
                    return false;
                }
            }
            $node = $this->pl_create_node($work_node);
            log_message('user', "进入流水线" . $pipe_name . " " . $node['node_code']);
            return $this->pl_add_do($pipe_name, $node);
        }
        return false;
    }

    /**
     * 插入流水线操作，不判断流水线是否有空位
     * @param $pipe_name
     * @param $node
     * @return bool
     */
    public function pl_add_do($pipe_name, $node)
    {
        $pipe = & $this->pipeline['db'][$pipe_name];

        if ($pipe['count'] == $pipe['current']) { //排在第一位，开始计时
            $node['start_time'] = $this->local_time;
        }

        $pipe['index_db'][$pipe['count']] = $node;
        //修改状态 一般为从  1 -> 2
        $this->cl_update_status($node['node_code'], 2, $pipe_name);

        $pipe['count']++; //流水线上当前作业数 +1
        $this->pipeline['work_count']++; //全部流水线上当前作业数 +1
        return true;
    }

    public function pl_refresh($pipe_name)
    {
        $pipe = & $this->pipeline['db'][$pipe_name];

        $arr_tmp = array(); //临时
        $arr_index_tmp = 0; //
        for ($i = 0; $i < sizeof($pipe['index_db']); $i++) {
            //判断超时
            if (isset($pipe['index_db'][$i]) && $pipe['index_db'][$i]) { //&& $i == $pipe['current']
                if ($i == $pipe['current']) {
                    if ($this->local_time >= $pipe['index_db'][$i]['start_time'] + $pipe['index_db'][$i]['cyle_time']) {
                        $this->hu_add($pipe_name, $pipe['index_db'][$i]); //进入挂起队列
                        $this->pl_del($pipe_name, $i); //出流水
                    } else {
                        //走时
                        $pipe['index_db'][$i]['use_time'] = $this->local_time - $pipe['index_db'][$i]['start_time'];

                        if ($pipe['index_db'][$i]) {
                            $arr_tmp[$arr_index_tmp++] = $pipe['index_db'][$i]; //准备移位
                        }
                    }
                } else {
                    if ($pipe['index_db'][$i]) {
                        $arr_tmp[$arr_index_tmp++] = $pipe['index_db'][$i]; //准备移位
                    }
                }

                $pipe['index_db'][$i] = false;
            }
        }

        //整理流水线上的位置， 一般用于位置前移
        for ($i = 0; $i < $arr_index_tmp; $i++) {
            if (isset($arr_tmp[$i]) && $arr_tmp[$i]) {
                if ($i == $pipe['current']) { //排在第一位，开始计时
                    if ($arr_tmp[$i]['start_time'] <= 0) {
                        $arr_tmp[$i]['start_time'] = $this->local_time;
                    }
                }
                $pipe['index_db'][$i] = $arr_tmp[$i];
            }
        }
    }

    /**
     * 从流水线中删除
     * @param $pipe_name
     * @param $pipe_index
     */
    public function pl_del($pipe_name, $pipe_index)
    {
        $this->pipeline['db'][$pipe_name]['index_db'][$pipe_index] = false;
        $this->pipeline['db'][$pipe_name]['count']--;
        $this->pipeline['work_count']--; //全部流水线上当前作业数 -1
    }

    /**
     * 出流水，通常是所有流水线一起找
     * @param $node_code 作业编号
     */
    public function pl_remove($node_code)
    {
        foreach ($this->pipeline['db'] as $pipe_name => $pipe) {
            foreach ($pipe['index_db'] as $k => $v) {
                if ($node_code == $v['node_code']) {
                    $this->pl_del($pipe_name, $k);
                }
            }
        }
    }

    /**
     * 将 $node_code 从$from流水线移动到$to流水线
     * @param $node_code
     * @param $from
     * @param $to
     */
    public function pl_move($node_code, $from, $to)
    {
        $pipe = & $this->pipeline['db'][$from];
        foreach ($pipe['index_db'] as $k => $v) {
            if ($node_code == $v['node_code']) {
                if ($v['start_time'] <= 0) {
                    $this->pl_add_do($to, $v);
                    $this->pl_del($from, $k);
                    $this->persistence(); //持久
                }
            }
        }
    }

    // 流水线  end
    /****************************************************************************************************/


    /****************************************************************************************************/
    // 挂起队列
    /****************************************************************************************************/
    public function hu_load()
    {
        $this->hang_up_list = array(
            'work_count' => 0, //全部挂起队列上当前作业数
            'pipe_count' => 0, //流水线 条数
            'db' => array(), //挂起队列
        );
        $ret = $this->pipeMod->get_pipe_data();
        if ($ret->get_code() === 0) {
            $pipe_arr = $ret->get_data();
            foreach ($pipe_arr as $pipe_node) {
                $hangup = array("count" => 0, 'index_db' => array());
                $hangup_ret = $this->persMod->get_pers_data('hangup_' . $pipe_node['pipe_name']);
                if ($hangup_ret->get_code() === 0) {
                    $hangup = $hangup_ret->get_data();
                }
                $this->hang_up_list['db'][$pipe_node['pipe_name']] = $hangup;
                $this->hang_up_list['work_count'] += $hangup['count'];
                $this->hang_up_list['pipe_count']++;
            }
        }
    }

    public function hu_add($pipe_name, $pipe_node)
    {
        $node = array(
            'hu_start' => $this->local_time, //进入挂起状态的时间
            'node_code' => $pipe_node['node_code'], //对应任务在work_list中的下标的数据
            'hu_time' => $this->local_time - $pipe_node['start_time'] - $pipe_node['cyle_time'], //已挂起时间
        );
        $hangup = & $this->hang_up_list['db'][$pipe_name];
        $hangup['index_db'][] = $node;
        $hangup['count']++; //流水线上当前作业数 +1
        $this->hang_up_list['work_count']++; //全部流水线上当前作业数 +1

        //修改状态 一般为从  2 -> 3
        $this->cl_update_status($pipe_node['node_code'], 3, $pipe_name);
        return true;
    }

    public function hu_remove($node_code)
    {
        foreach ($this->hang_up_list['db'] as $pipe_name => $hangup) {
            foreach ($hangup['index_db'] as $k => $v) {
                if ($node_code == $v['node_code']) {
                    unset($this->hang_up_list['db'][$pipe_name]['index_db'][$k]);
                    $this->hang_up_list['db'][$pipe_name]['count']--;
                    $this->hang_up_list['work_count']--; //全部流水线上当前作业数 -1
                }
            }
        }
    }

    public function hu_refresh()
    {
        foreach ($this->hang_up_list['db'] as $pipe_name => $hangup) {
            foreach ($hangup['index_db'] as $k => $v) {
                $hangup_node = & $this->hang_up_list['db'][$pipe_name]['index_db'][$k];
                $hangup_node['hu_time'] = $this->local_time - $hangup_node['hu_start'];
            }
        }
    }

    /**
     * 挂起队列持久
     */
    public function hu_persistence()
    {
        foreach ($this->hang_up_list['db'] as $pipe_name => $hangup) {
            $this->persMod->persistence('hangup_' . $pipe_name, $hangup);
        }
    }

    // 挂起队列  end
    /****************************************************************************************************/

    public function hello3()
    {
        $this->hu_refresh();
        print_r($this->hang_up_list);
    }

    public function hello2()
    {
        $this->print_list();

//        $work_node = $this->wl_get_first();
//        $this->pl_add('pipe_a', $work_node);
//        $work_node = $this->wl_get_first();
//        $this->pl_add('pipe_a', $work_node);
//        $work_node = $this->wl_get_first();
//        $this->pl_add('pipe_a', $work_node);

//        $this->pl_remove(4);
        $this->pl_refresh('pipe_a');
        print_r($this->pipeline);
        $this->persistence();
    }

    public function hello()
    {
        echo 'helloworld';
        return false;
//        $start_time = time(); //////////////////////////////
//        echo $start_time . '<p/>'; ///////////////////////
        $work_node = array(
            'node_weight' => 1,
            'node_code' => "D0001",
        );
        $info = array(
            'order_id' => 10001,
            'work_id' => 1,
        );
        $sum = 1;
        for ($i = 0; $i < $sum; $i++) {
            $this->wl_add(1, $this->wl_create_node($work_node), $info);
        }


        $work_node = array(
            'node_weight' => 3,
            'node_code' => "D0002",
        );
        $info = array(
            'order_id' => 10002,
            'work_id' => 2,
        );
        $this->wl_add(3, $this->wl_create_node($work_node), $info);


        $work_node = array(
            'node_weight' => 2,
            'node_code' => "D0003",
        );
        $info = array(
            'order_id' => 10003,
            'work_id' => 3,
        );
        $this->wl_add(2, $this->wl_create_node($work_node), $info);

        $work_node = array(
            'node_weight' => 4,
            'node_code' => "D0004",
        );
        $info = array(
            'order_id' => 10004,
            'work_id' => 4,
        );
        $this->wl_add(4, $this->wl_create_node($work_node), $info);

        $work_node = array(
            'node_weight' => 3,
            'node_code' => "D0005",
        );
        $info = array(
            'order_id' => 10005,
            'work_id' => 5,
        );
        $this->wl_add(3, $this->wl_create_node($work_node), $info);

        //  $this->wl_remove(2);
        //    $this->wl_get_first();
        $this->print_list();
        $this->persistence();


//        $end_time = time();
//        echo $end_time . '<p/>';
//        echo $end_time - $start_time;
        echo '<p/>' . $this->work_list['count'];
        echo '<p/>';
        //   $this->wl_adjust(4, 0);
//        $this->print_list();
        echo '<p/>';
        echo $this->cl_get_all_codes();

    }

    function print_list()
    {
        $p = $this->work_list['start'];
        do {
            $node_tmp = $this->wl_get($p);
            echo $p . '  ' . $node_tmp['node_code'] . '<p/>';
            $p = $node_tmp['next_p'];

        } while ($p > 0);
    }

    /**
     * 初始化参数
     * 创建schedule对象时调用
     * @param array $config
     */
    function initialize($config = array())
    {
        foreach ($config as $key => $val) {
            if (isset($this->$key)) {
                $this->$key = $val;
            }
        }
    }
}